package com.yjd.comm.web.session;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.yjd.comm.util.FrameUtil;
import org.apache.log4j.Logger;

import com.yjd.comm.cache.CachedUtil;
import com.yjd.comm.util.DateUtil;
import com.yjd.comm.util.ServletUtil;
import com.yjd.comm.util.StringUtil;

/**
 * 使用memcached作为session服务器，可作为集群使用
 * 
 * @author ready
 * 
 */
public class MemcachedSessionServiceImpl implements ISessionService {
	private Logger logger = Logger.getLogger(MemcachedSessionServiceImpl.class);
	private boolean isSessionCached = true;
	/**
	 * session刷新时间，默认五分钟
	 */
	private long sessionFlushTime = 300000;
	private static MemcachedSessionServiceImpl sessionService;

	private MemcachedSessionServiceImpl() {

	}

	public static MemcachedSessionServiceImpl getInstance() {
		if (sessionService == null) {
			synchronized (MemcachedSessionServiceImpl.class) {
				if (sessionService == null) {
					sessionService = new MemcachedSessionServiceImpl();
				}
			}
		}
		return sessionService;
	}

	@Override
	public Map<String, Object> getSessionMap(HttpServletRequest request,
			HttpServletResponse response) {
		return getSessionMap(request, response, true);
	}

	@Override
	public Map<String, Object> getSessionMap(HttpServletRequest request,
			HttpServletResponse response, boolean notExistsIsCreate) {
		String sessionId = getSessionId(request, response);
		if (StringUtil.isNotEmpty(sessionId)) {
			return getSessionMap(sessionId);
		} else {
			if (notExistsIsCreate) {
				return getSessionMap(initSessionId(request, response));
			} else {
				return null;
			}
		}
	}

	private Date getSessionTimeOut() {
		return DateUtil.getDateAfter(sessionTimeOut());
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getSessionMap(String sessionId) {
		long currentTime = System.currentTimeMillis();
		Map<String, Object> map = null;
		if (sessionId != null) {
			map = (Map<String, Object>) CachedUtil.defaultCached(
					isSessionCached).get(sessionId);
		}
		logger.debug("get object from memcached, session:" + sessionId
				+ " result: " + (map != null));
		if (map == null) {
			map = new HashMap<String, Object>();
			map.put("LAST_UPDATE_TIME", currentTime);
			Date date = getSessionTimeOut();
			map.put("TIMEOUT",
					DateUtil.format(date, DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
			CachedUtil.defaultCached(isSessionCached).set(sessionId, date, map);
		} else {
			flushBySessionId(sessionId);
		}
		if (map.get("ver") == null) {
			map.put("ver", 0);
		}
		return map;
	}

	private String getLastUpdateTimeKey(String sessionId) {
		return sessionId + "_LAST_UPDATE_TIME";
	}

	@SuppressWarnings("unchecked")
	private void flushBySessionId(String sessionId) {
		long currentTime = System.currentTimeMillis();
		Object obj = CachedUtil.defaultCached(isSessionCached).get(
				getLastUpdateTimeKey(sessionId));
		if (obj != null) {
			long updateTime = Long.parseLong(obj.toString());
			long period = currentTime - updateTime;
			if (period > sessionFlushTime) {
				Map<String, Object> map = (Map<String, Object>) CachedUtil
						.defaultCached(isSessionCached).get(sessionId);
				logger.debug("get object from memcached, session:" + sessionId
						+ " result: " + (map != null));
				Date date = getSessionTimeOut();
				if (map == null) {
					map = new HashMap<String, Object>();
					map.put("TIMEOUT", DateUtil.format(date,
							DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
					CachedUtil.defaultCached(isSessionCached).set(sessionId,
							date, map);
				} else {
					CachedUtil.defaultCached(isSessionCached).replace(
							sessionId, date, map);
				}
				CachedUtil.defaultCached(isSessionCached).set(
						getLastUpdateTimeKey(sessionId), date, currentTime);
			}
		}
	}

	/**
	 * 把对象放置到Session
	 * 
	 * @param request
	 * @param key
	 * @param obj
	 */
	public void putSession(HttpServletRequest request,
			HttpServletResponse response, String key, Object obj) {
		String sessionId = getSessionId(request, response);
		Map<String, Object> map = getSessionMap(sessionId);
		map.put("LAST_UPDATE_TIME", System.currentTimeMillis());
		map.put(key, obj);
		Date date = getSessionTimeOut();
		map.put("TIMEOUT",
				DateUtil.format(date, DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
		boolean result = CachedUtil.defaultCached(isSessionCached).set(
				sessionId, date, map);
		CachedUtil.defaultCached(isSessionCached).set(
				getLastUpdateTimeKey(sessionId), date,
				System.currentTimeMillis());
		logger.debug("put object to memcached, session:" + sessionId
				+ " result: " + result);
	}

	/**
	 * 从Session中取对象
	 * 
	 * @param request
	 * @param key
	 * @return
	 */
	public Object getSession(HttpServletRequest request,
			HttpServletResponse response, String key) {
		try {
			String sessionId = getSessionId(request, response);
			Map<String, Object> map = getSessionMap(sessionId);
			return map.get(key);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 从Session中删除对象
	 * 
	 * @param request
	 * @param key
	 */
	public void removeSession(HttpServletRequest request,
			HttpServletResponse response, String key) {
		String sessionId = getSessionId(request, response);
		if (StringUtil.isNotEmpty(sessionId)) {
			Map<String, Object> map = getSessionMap(sessionId);
			Object obj = map.get(key);
			logger.debug("REMOVE SESSION : key: " + key + " value : " + obj
					+ " from " + sessionId);
			map.remove(key);
			int ver = Integer.parseInt(map.get("ver").toString());
			map.put("ver", ++ver);
			Date date = getSessionTimeOut();
			map.put("TIMEOUT",
					DateUtil.format(date, DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
			CachedUtil.defaultCached(isSessionCached).replace(sessionId, date,
					map);
		}
	}

	public String getSessionId(HttpServletRequest request,
			HttpServletResponse response) {
		if (request.getAttribute(ServletUtil.getSessionCookieName()) != null) {
			return request.getAttribute(ServletUtil.getSessionCookieName())
					.toString();
		} else {
			return ServletUtil.getCookieValue(request,
					ServletUtil.getSessionCookieName());
		}
	}

	/**
	 * 初始化sessionId
	 * 
	 * @param request
	 * @param response
	 */
	public String initSessionId(HttpServletRequest request,
			HttpServletResponse response) {
		Cookie cookie = ServletUtil.getCookie(request,
				ServletUtil.getSessionCookieName());
		String sessionId = cookie == null ? null : cookie.getValue();
		if (sessionId == null) {
			if (StringUtil.isNotEmpty(request.getParameter(ServletUtil
					.getSessionCookieName()))) {
				sessionId = request.getParameter(ServletUtil
						.getSessionCookieName());
			} else {
				sessionId = ServletUtil.getSessionCookieName()
						+ UUID.randomUUID().toString() + "_" + System.currentTimeMillis() + "_" + ServletUtil.getIpAddr(request);
			}
			logger.info("===================sessionId:"+sessionId);
			boolean useHost = ServletUtil.getRequestUrl(request).toString()
					.contains(ServletUtil.domainHost());
			ServletUtil.addCookie(response, ServletUtil.getSessionCookieName(),
					sessionId, ServletUtil.getSessionTimeOut(), useHost, true);
			request.setAttribute(ServletUtil.getSessionCookieName(), sessionId);
		}
		return sessionId;
	}

	@Override
	public void flush(HttpServletRequest request, HttpServletResponse response) {
		flushBySessionId(getSessionId(request, response));
	}

	@Override
	public int sessionTimeOut() {
		return ServletUtil.getSessionTimeOut();
	}

	@Override
	public void cleanAllSession(HttpServletRequest request,
			HttpServletResponse response, boolean isRemoveSessionId) {
		String sessionId = getSessionId(request, response);
		if (StringUtil.isNotEmpty(sessionId)) {
			CachedUtil.defaultCached(isSessionCached).remove(sessionId);
			if (isRemoveSessionId) {
				boolean useHost = ServletUtil.getRequestUrl(request).toString()
						.contains(ServletUtil.domainHost());
				logger.info("userHost:" + useHost);
				ServletUtil.deleteCookie(request, response, ServletUtil
						.getSessionCookieName(),
						useHost ? ServletUtil.domainHost() : null, true);
			}
		}
	}

	@Override
	public Object getAttribute(HttpServletRequest request,
			HttpServletResponse response, String key) {
		Map<String, Object> map = getSessionMap(request, response);
		return map != null ? map.get(key) : null;
	}

}
